package com.crp.unitalinforbaseplat.app.core.config

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect
import com.crp.unitalinforbaseplat.app.core.MyShiroSessionListener
import com.crp.unitalinforbaseplat.app.sys.security.MyRealm
import com.crp.unitalinforbaseplat.app.unitalinforbaseplat.entity.StringToDateConverter
import org.apache.shiro.authc.credential.HashedCredentialsMatcher
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
import org.apache.shiro.authc.pam.ModularRealmAuthenticator
import org.apache.shiro.codec.Base64
import org.apache.shiro.mgt.SecurityManager
import org.apache.shiro.realm.Realm
import org.apache.shiro.session.SessionListener
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler
import org.apache.shiro.spring.LifecycleBeanPostProcessor
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor
import org.apache.shiro.spring.web.ShiroFilterFactoryBean
import org.apache.shiro.web.mgt.CookieRememberMeManager
import org.apache.shiro.web.mgt.DefaultWebSecurityManager
import org.apache.shiro.web.servlet.SimpleCookie
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.convert.support.GenericConversionService
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
import java.util.LinkedHashMap
import javax.annotation.PostConstruct
import kotlin.collections.ArrayList
import kotlin.collections.Collection

/**
 * @program: squarenavigationsys
 * @description:shiro参数配置
 * @author:  chenzhicai
 * @create: 2018-11-03-20-20
 **/
@Configuration
open class ShiroConfig {

    @Autowired lateinit var handlerAdapter:RequestMappingHandlerAdapter

    @PostConstruct
    open fun initEditableValidate(){
        val initilaize = handlerAdapter.webBindingInitializer as ConfigurableWebBindingInitializer
        if(initilaize.conversionService != null){
            val genericConver = initilaize.conversionService as GenericConversionService
            genericConver.addConverter(StringToDateConverter())
        }
    }


    @Bean
    open fun servletContainer(): TomcatServletWebServerFactory {
        val factory = TomcatServletWebServerFactory()
        val contextCustomizer = TomcatContextCustomizer { context -> context.addWelcomeFile("/view/login.html") }
        factory.addContextCustomizers(contextCustomizer);
        return factory;
    }

    /**
     * 文件上传临时路径
     */
//    @Bean
//    open fun multipartConfigElement(): MultipartConfigElement {
//        val factory = MultipartConfigFactory();
//        factory.setLocation("/app/pttms/files");
//        return factory.createMultipartConfig();
//    }

    @Bean(name = arrayOf("shiroFilter"))
    open fun shiroFilter(): ShiroFilterFactoryBean {
        println("ShiroConfiguration.shirFilter()")
        val shiroFilterFactoryBean = ShiroFilterFactoryBean()
        shiroFilterFactoryBean.securityManager = securityManager()
        //配置拦截器
        val filterChainDefinitionMap = LinkedHashMap<String, String>()
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/assets/**", "anon")
        filterChainDefinitionMap.put("/unitlinforbase/index.crp", "anon")       //登陆页面匿名可以访问
        filterChainDefinitionMap.put("/backend/login.html", "anon")    //登陆页面匿名可以访问
        filterChainDefinitionMap.put("/backend/**", "authc")   //常规页面里面的必须认证才可以访问
//        filterChainDefinitionMap.put("","")
//        filterChainDefinitionMap.put("/*", "authc")//表示需要认证才可以访问
//        filterChainDefinitionMap.put("/**", "authc");//表示需要认证才可以访问
//        filterChainDefinitionMap.put("/*.*", "authc")
        // 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了


        filterChainDefinitionMap.put("/logout", "logout");
        // 如果不设置默认会自动寻找Web工程根目录下的"/view/login"页面
        // 登录的地方必须设置login，否则过滤器会过滤掉
        shiroFilterFactoryBean.setLoginUrl("/user/login")
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/")
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403")
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);


        return shiroFilterFactoryBean
    }

    /***
     *@Description Relam实现
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:11
     ***/
    @Bean
    open fun myRealm(): MyRealm {
        val myRelam = MyRealm()
//        myRelam.credentialsMatcher = hashedCredentialsMatcher()
        return myRelam
    }

    /**
     *@Description 加密方式
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:12
     **/
    @Bean
    open fun hashedCredentialsMatcher(): HashedCredentialsMatcher {
        val hashedCredentialsMatcher = HashedCredentialsMatcher()
        hashedCredentialsMatcher.hashAlgorithmName = "MD5"// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.hashIterations = 1// 散列的次数，比如散列两次，相当于md5(md5(""));
        hashedCredentialsMatcher.isStoredCredentialsHexEncoded = true// 表示是否存储散列后的密码为16进制，需要和生成密码时的一样，默认是base64；
        return hashedCredentialsMatcher

    }

    /**
     *@Description 会话管理器
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:14
     **/
    @Bean(name = arrayOf("securityManager"))
    open fun securityManager(): DefaultWebSecurityManager {
        val securityManager = DefaultWebSecurityManager()
//        securityManager.setCacheManager(cacheManager());
        securityManager.authenticator = authenticator()
        securityManager.rememberMeManager = rememberMeManager()
        securityManager.realms = realms()
//        securityManager.setSessionManager(sessionManager());
        return securityManager
    }

    /**
     *@Description 返回Relam池
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:19
     **/
    @Bean
    open fun realms(): kotlin.collections.Collection<Realm> {
        val realms = ArrayList<Realm>()
        realms.add(myRealm())
        return realms
    }

//    @Bean
//    open fun cacheManager():EhCacheManager{
//
//    }

    /**
     *@Description 官方文档 <tt>AuthenticationStrategy</tt> implementation that requires <em>at least one</em> configured realm to
     *             successfully process the submitted <tt>AuthenticationToken</tt> during the log-in attempt.
     *            即得至少要求又一个Realm,如果含有多个Realm，就回配置所有的Realm
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:21
     **/
    @Bean
    open fun authenticationStrategy(): AtLeastOneSuccessfulStrategy {
        return AtLeastOneSuccessfulStrategy()
    }

    /**
     *@Description 当只有一个Realm时，就使用这个Realm，当配置了多个Realm时，会使用所有配置的Realm。
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:22
     **/
    @Bean
    open fun authenticator(): ModularRealmAuthenticator {
        val authenticator = ModularRealmAuthenticator()
        authenticator.authenticationStrategy = authenticationStrategy()
        return authenticator
    }

    /**
     *@Description
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:25
     **/
    @Bean
    open fun rememberMeCookie(): SimpleCookie {
        // 这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        val simpleCookie = SimpleCookie("rememberMe")
        // <!-- 记住我cookie生效时间30天 ,单位秒;-->
        simpleCookie.maxAge = 259200
        return simpleCookie
    }

    @Bean
    open fun rememberMeManager(): CookieRememberMeManager {
        println("ShiroConfiguration.rememberMeManager()")
        val cookieRememberMeManager = CookieRememberMeManager()
        cookieRememberMeManager.cookie = rememberMeCookie()
        cookieRememberMeManager.cipherKey = Base64.decode("2AvVhdsgUs0FSA3SDFAdag==")
        return cookieRememberMeManager
    }

    @Bean
    open fun myShiroSessionListener(): MyShiroSessionListener {
        return MyShiroSessionListener()
    }

    /**
     *@Description 会话监听器
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:48
     **/
    @Bean
    open fun sessionListeners(): Collection<SessionListener> {
        val listeners = ArrayList<SessionListener>()
        listeners.add(myShiroSessionListener())
        return listeners
    }


    /**
     *@Description 会话ID生成器
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:32
     **/
    @Bean
    open fun sessionIdGenerator(): Collection<SessionListener> {
        val listeners = ArrayList<SessionListener>();
        listeners.add(myShiroSessionListener())
        return listeners
    }


//    @Bean
//    open fun mySessionDao() {
//
//    }

    /**
     *@Description 处理Session有效期
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:41
     **/
    @Bean
    open fun sessionValidationScheduler(): ExecutorServiceSessionValidationScheduler {
        val sessionValidationScheduler = ExecutorServiceSessionValidationScheduler()
        sessionValidationScheduler.interval = 1800000
        return sessionValidationScheduler
    }

    /**
     *@Description 会话管理
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:43
     **/
    @Bean(name = arrayOf("sessionManager"))
    open fun sessionManager(): DefaultWebSessionManager {
        println("ShiroConfiguration.sessionManager()")
        val sessionManager = DefaultWebSessionManager()
        sessionManager.sessionIdCookie.name = "sId"
        sessionManager.globalSessionTimeout = 1800000
        sessionManager.isDeleteInvalidSessions = true
        sessionManager.sessionValidationScheduler = sessionValidationScheduler()
        sessionManager.isSessionValidationSchedulerEnabled = true
        sessionManager.sessionListeners = sessionListeners()
//        sessionManager.setSessionDAO(mySessionDao())
        return sessionManager
    }


    /**
     *@Description 开启shiro注解 ---- 注解权限
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:37
     **/
    @Bean
    open fun authorizationAttributeSourceAdvisor(securityManager: SecurityManager): AuthorizationAttributeSourceAdvisor {
        println("ShiroConfiguration.authorizationAttributeSourceAdvisor()")
        val authorizationAttributeSourceAdvisor = AuthorizationAttributeSourceAdvisor()
        authorizationAttributeSourceAdvisor.securityManager = securityManager
        return authorizationAttributeSourceAdvisor
    }

    /**
     *@Description Shiro生命周期处理器 ---可以自定的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法.
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:37
     **/
    @Bean(name = arrayOf("lifecycleBeanPostProcessor"))
    open fun lifecycleBeanPostProcessor(): LifecycleBeanPostProcessor {
        return LifecycleBeanPostProcessor()
    }


    /**
     *@Description 开启shiro注解 ----启用 IOC 容器中使用 shiro 的注解. 但必须在配置了 LifecycleBeanPostProcessor
     *           之后才可以使用
     *@Params
     *@return
     *@Author chenzhicai
     *@Date 2018/8/31
     *@Time 上午11:39
     **/
    @Bean
    open fun getDefaultAdvisorAutoProxyCreator(): DefaultAdvisorAutoProxyCreator {
        val daap = DefaultAdvisorAutoProxyCreator()
        daap.isProxyTargetClass = true
        return daap
    }

    @Bean
    open fun shiroDialect(): ShiroDialect {
        return ShiroDialect()
    }

//    @Bean(name = arrayOf("templateResolver"))
////    open fun templateEngine(): SpringTemplateEngine {
////
////        val templateEngine = SpringTemplateEngine()
////        templateEngine.setTemplateResolver(TemplateResolver());
////
////        val additionalDialects = HashSet<IDialect>();
////
////        additionalDialects.add(ShiroDialect())
////
////        templateEngine.setAdditionalDialects(additionalDialects)
////        return templateEngine
////    }

}